<template>
  <div ref="editor" class="code-editor"></div>
</template>

<script setup lang="ts">
import { ref, watch, onMounted, onBeforeUnmount, defineProps, defineEmits } from 'vue';
import { EditorView, basicSetup } from '@codemirror/basic-setup';
import { EditorState } from '@codemirror/state';
import { oneDark } from '@codemirror/theme-one-dark';
import { javascript } from '@codemirror/lang-javascript';
import { xml } from '@codemirror/lang-xml';
import { css } from '@codemirror/lang-css';

const props = defineProps<{
  modelValue: string;
  lang?: 'javascript' | 'xml' | 'css';
}>();
const emit = defineEmits(['update:modelValue', 'change']);

const editor = ref<HTMLDivElement | null>(null);
let view: EditorView | null = null;

const getLangExtension = () => {
  switch (props.lang) {
    case 'javascript': return javascript();
    case 'xml': return xml();
    case 'css': return css();
    default: return javascript();
  }
};

onMounted(() => {
  if (editor.value) {
    view = new EditorView({
      state: EditorState.create({
        doc: props.modelValue,
        extensions: [
          basicSetup,
          oneDark,
          getLangExtension(),
          EditorView.updateListener.of((v) => {
            if (v.docChanged) {
              const value = v.state.doc.toString();
              emit('update:modelValue', value);
              emit('change', value);
            }
          }),
        ],
      }),
      parent: editor.value,
      });
  }
});

watch(
  () => props.modelValue,
  (val) => {
    if (view && view.state.doc.toString() !== val) {
      view.dispatch({
        changes: { from: 0, to: view.state.doc.length, insert: val || '' },
      });
    }
  }
);

onBeforeUnmount(() => {
  if (view) {
    view.destroy();
    view = null;
  }
});
</script>

<style scoped>
  .code-editor {
  min-height: 200px;
  border: 1px solid #333;
  border-radius: 4px;
  font-size: 14px;
  background: #282c34;
  }
</style>
